package org.openkanban.server.dao.impl;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.openkanban.server.dao.ActivityDao;
import org.openkanban.server.domain.Activity;
import org.openkanban.server.domain.Kanban;
import org.openkanban.shared.util.StatusCodes;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

/**
 * jpa implementation of activity data access object
 */
@Repository
public class ActivityDaoJpaImpl implements ActivityDao {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    @Transactional
    public Activity createActivity(Activity activity) {
	entityManager.persist(activity);
	return activity;
    }

    @Override
    public Activity getActivity(Long activityId) {
	return entityManager.find(Activity.class, activityId);
    }

    @Override
    public Activity getActivityByKanbanId(Long kanbanId) {
	Query query = entityManager.createQuery("SELECT a FROM Activity a JOIN a.kanbans k WHERE k.kanbanId=:kanbanId");
	query.setParameter("kanbanId", kanbanId);

	return (Activity) query.getSingleResult();
    }

    @Override
    @Transactional
    public Kanban addKanbanToActivity(Long parentActivityId, Kanban newKanban) {
	Activity parentActivity = entityManager.find(Activity.class, parentActivityId);
	if (parentActivity != null) {
	    entityManager.persist(newKanban);
	    parentActivity.getKanbans().add(newKanban);
	    return newKanban;
	}
	return null;
    }

    @Override
    public List<Activity> getAll() {
	Query query = entityManager.createQuery("SELECT a FROM Activity a");
	@SuppressWarnings("unchecked")
	List<Activity> activityList = query.getResultList();
	return activityList;
    }

    @Override
    @Transactional
    public StatusCodes removeActivity(Activity activity) {
	entityManager.remove(activity);
	return StatusCodes.OK;
    }

    @Override
    @Transactional
    public Activity updateActivity(Activity activity) {
	Activity activityToUpdate = entityManager.find(Activity.class, activity.getActivityId());
	activityToUpdate.setActivityName(activity.getActivityName());
	activityToUpdate.setWipLimit(activity.getWipLimit());
	return activityToUpdate;
    }

    @Override
    @Transactional
    public Kanban moveKanbanToActivity(Long parentActivityId, Long kanbanId) {
	Query query = entityManager.createQuery("SELECT a FROM Activity a JOIN a.kanbans k WHERE k.kanbanId=:kanbanId");
	query.setParameter("kanbanId", kanbanId);
	Activity sourceActivity = (Activity) query.getSingleResult();
	Activity destActivity = entityManager.find(Activity.class, parentActivityId);
	Kanban kanban = entityManager.find(Kanban.class, kanbanId);
	if (sourceActivity != null && destActivity != null && kanban != null) {
	    sourceActivity.getKanbans().remove(kanban);
	    destActivity.getKanbans().add(kanban);
	    entityManager.merge(sourceActivity);
	    entityManager.merge(destActivity);
	    return kanban;
	}
	return null;
    }

    @Override
    public List<Activity> getAllByBoardId(Long parentBoardId) {
	Query query = entityManager.createQuery("SELECT a FROM Board b JOIN b.activities a WHERE boardId=:boardId");
	query.setParameter("boardId", parentBoardId);
	@SuppressWarnings("unchecked")
	List<Activity> activityList = query.getResultList();
	return activityList;
    }

    @Override
    public List<Activity> getActivityRange(Long boardId, Long beginActivityId, Long endActivityId) {
	Query query = entityManager
		.createQuery("SELECT a FROM Board b JOIN b.activities a WHERE b.boardId=:boardId AND ((a.activityId BETWEEN :beginActivityId AND :endActivityId) OR (a.activityId BETWEEN :endActivityId AND :beginActivityId))");
	query.setParameter("boardId", boardId);
	query.setParameter("beginActivityId", beginActivityId);
	query.setParameter("endActivityId", endActivityId);

	@SuppressWarnings("unchecked")
	List<Activity> resultList = (List<Activity>) query.getResultList();
	return resultList;
    }
}
